//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package impl

import (
	"context"
	"fmt"

	"atomgit.com/openlinksaas/sql-proxy/gen_code"
)

type TableApiImpl struct{}

func (s *TableApiImpl) TableListPost(ctx context.Context, req gen_code.TableListPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}

	infoItem, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	info := infoItem.(*ClientInfo)

	tableNameList := []string{}
	if info.Driver == "mysql" {
		rows, err := info.client.Query("SHOW TABLES FROM " + req.Db)
		if err != nil {
			return gen_code.ImplResponse{
				Code: 500,
				Body: gen_code.ErrInfo{
					ErrMsg: err.Error(),
				},
			}, nil
		}
		defer rows.Close()

		for rows.Next() {
			tableName := ""
			err = rows.Scan(&tableName)
			if err != nil {
				return gen_code.ImplResponse{
					Code: 500,
					Body: gen_code.ErrInfo{
						ErrMsg: err.Error(),
					},
				}, nil
			}
			tableNameList = append(tableNameList, tableName)
		}
	} else if info.Driver == "postgres" {
		client, err := info.NewPgClientWithDb(req.Db)
		if err != nil {
			return gen_code.ImplResponse{
				Code: 500,
				Body: gen_code.ErrInfo{
					ErrMsg: err.Error(),
				},
			}, nil
		}
		defer client.Close()

		rows, err := client.Query("SELECT tablename FROM pg_tables where schemaname not in ('information_schema', 'pg_catalog')")
		if err != nil {
			return gen_code.ImplResponse{
				Code: 500,
				Body: gen_code.ErrInfo{
					ErrMsg: err.Error(),
				},
			}, nil
		}
		defer rows.Close()

		for rows.Next() {
			tableName := ""
			err = rows.Scan(&tableName)
			if err != nil {
				return gen_code.ImplResponse{
					Code: 500,
					Body: gen_code.ErrInfo{
						ErrMsg: err.Error(),
					},
				}, nil
			}
			tableNameList = append(tableNameList, tableName)
		}
	}
	return gen_code.ImplResponse{
		Code: 200,
		Body: tableNameList,
	}, nil

}

func (s *TableApiImpl) TableQueryPost(ctx context.Context, req gen_code.TableQueryPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}

	infoItem, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	info := infoItem.(*ClientInfo)
	sqlStr := ""
	if req.Where != "" {
		sqlStr = fmt.Sprintf("SELECT count(*) FROM %s.%s WHERE %s", req.Db, req.Table, req.Where)
	} else {
		sqlStr = fmt.Sprintf("SELECT count(*) FROM %s.%s;", req.Db, req.Table)
	}
	client := info.client
	var err error
	if info.Driver == "postgres" {
		client, err = info.NewPgClientWithDb(req.Db)
		if err != nil {
			return gen_code.ImplResponse{
				Code: 500,
				Body: gen_code.ErrInfo{
					ErrMsg: err.Error(),
				},
			}, nil
		}
		defer client.Close()
		if req.Where != "" {
			sqlStr = fmt.Sprintf("SELECT count(*) FROM %s WHERE %s", req.Table, req.Where)
		} else {
			sqlStr = fmt.Sprintf("SELECT count(*) FROM %s;", req.Table)
		}
	}
	rows, err := client.Query(sqlStr)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	defer rows.Close()

	rows.Next()
	count := int64(0)
	err = rows.Scan(&count)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}

	sqlStr2 := ""
	if req.Where != "" {
		sqlStr2 = fmt.Sprintf("SELECT * FROM %s.%s WHERE %s LIMIT %d OFFSET %d", req.Db, req.Table, req.Where, req.Limit, req.Offset)
	} else {
		sqlStr2 = fmt.Sprintf("SELECT * FROM %s.%s LIMIT %d OFFSET %d", req.Db, req.Table, req.Limit, req.Offset)
	}
	if info.Driver == "postgres" {
		if req.Where != "" {
			sqlStr2 = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT %d OFFSET %d", req.Table, req.Where, req.Limit, req.Offset)
		} else {
			sqlStr2 = fmt.Sprintf("SELECT * FROM %s LIMIT %d OFFSET %d", req.Table, req.Limit, req.Offset)
		}
	}

	rows2, err := client.Query(sqlStr2)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	defer rows2.Close()

	columnList, err := rows2.ColumnTypes()
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	columns := []gen_code.Column{}
	for _, column := range columnList {
		nullAble, _ := column.Nullable()
		columns = append(columns, gen_code.Column{
			Name:     column.Name(),
			TypeName: column.DatabaseTypeName(),
			NullAble: nullAble,
		})
	}

	values := [][]string{}
	for rows2.Next() {
		row := []any{}
		for i := 0; i < len(columnList); i++ {
			v := ""
			row = append(row, &v)
		}
		rows2.Scan(row...)
		rowValueList := []string{}
		for _, v := range row {
			sv := v.(*string)
			if sv == nil {
				rowValueList = append(rowValueList, "(null)")
			} else {
				rowValueList = append(rowValueList, *sv)
			}
		}
		values = append(values, rowValueList)
	}

	return gen_code.ImplResponse{
		Code: 200,
		Body: &gen_code.TableQueryPost200Response{
			Count:   int32(count),
			Columns: columns,
			Values:  values,
		},
	}, nil
}

func (s *TableApiImpl) TableRawQueryPost(ctx context.Context, req gen_code.TableRawQueryPostRequest) (gen_code.ImplResponse, error) {
	if req.Token != token {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: "wrong token",
			},
		}, nil
	}

	infoItem, ok := clientMap.Load(req.Id)
	if !ok {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: "miss client",
			},
		}, nil
	}
	info := infoItem.(*ClientInfo)

	client := info.client
	var err error
	if info.Driver == "postgres" {
		client, err = info.NewPgClientWithDb(req.Db)
		if err != nil {
			return gen_code.ImplResponse{
				Code: 500,
				Body: gen_code.ErrInfo{
					ErrMsg: err.Error(),
				},
			}, nil
		}
		defer client.Close()
	}

	rows, err := client.Query(req.Sql)
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	defer rows.Close()

	columnList, err := rows.ColumnTypes()
	if err != nil {
		return gen_code.ImplResponse{
			Code: 500,
			Body: gen_code.ErrInfo{
				ErrMsg: err.Error(),
			},
		}, nil
	}
	columns := []gen_code.Column{}
	for _, column := range columnList {
		nullAble, _ := column.Nullable()
		columns = append(columns, gen_code.Column{
			Name:     column.Name(),
			TypeName: column.DatabaseTypeName(),
			NullAble: nullAble,
		})
	}

	values := [][]string{}
	for rows.Next() {
		row := []any{}
		for i := 0; i < len(columnList); i++ {
			v := ""
			row = append(row, &v)
		}
		rows.Scan(row...)
		rowValueList := []string{}
		for _, v := range row {
			sv := v.(*string)
			if sv == nil {
				rowValueList = append(rowValueList, "(null)")
			} else {
				rowValueList = append(rowValueList, *sv)
			}
		}
		values = append(values, rowValueList)
	}
	return gen_code.ImplResponse{
		Code: 200,
		Body: &gen_code.TableRawQueryPost200Response{
			Columns: columns,
			Values:  values,
		},
	}, nil
}
